package com.authority.service.impl;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.authority.OpServiceException;
import com.authority.common.AuthConstants;
import com.authority.common.StatusEnum;
import com.authority.dao.AuthActionDAO;
import com.authority.dao.AuthMenuActionDAO;
import com.authority.dao.AuthMenuDAO;
import com.authority.dao.AuthRoleMenuDAO;
import com.authority.dao.AuthSysDAO;
import com.authority.dao.AuthUserDAO;
import com.authority.entry.AuthActionDO;
import com.authority.entry.AuthMenuActionDO;
import com.authority.entry.AuthMenuDO;
import com.authority.entry.AuthRoleDO;
import com.authority.entry.AuthRoleMenuDO;
import com.authority.query.AuthMenuQuery;
import com.authority.result.PageResult;
import com.authority.service.AuthMenuService;
import com.authority.tree.Node;
import com.authority.tree.NodeUtils;
import com.authority.utils.CommonUtils;
import com.authority.utils.CommonUtils.CallBack;
import com.authority.utils.SuixunAssert;
import com.authority.vo.AuthMenuVO;
import com.authority.vo.AuthRoleVO;
import com.authority.vo.IdNameVO;
import com.google.common.collect.Lists;
import com.suixun.util.Constant;
import com.suixun.util.StringUtil;

@Service("authMenuService")
@Transactional(rollbackFor = Exception.class)
public class AuthMenuServiceImpl implements AuthMenuService {

	@Resource
	private AuthMenuDAO authMenuDAO;

	@Resource
	private AuthMenuActionDAO authMenuActionDAO;

	@Resource
	private AuthActionDAO authActionDAO;

	@Resource
	private AuthSysDAO authSysDAO;

	@Resource
	private AuthRoleMenuDAO authRoleMenuDAO;

	@Resource
	private AuthUserDAO userDAO;

	@Override
	public void saveAuthMenu(AuthMenuDO db, List<AuthMenuActionDO> list) throws Exception {

		// 检查编码是否重复
		if (authMenuDAO.queryAuthMenuByCodeAndSysId(db.getCode(), db.getSysId()) != null) {
			throw new Exception("菜单编码已存在");
		}

		// 添加菜单信息
		Long menuId = authMenuDAO.insertAuthMenu(db);

		// 批量添加功能
		if (!CollectionUtils.isEmpty(list)) {
			List<AuthMenuActionDO> newList = Lists.newArrayList();
			for (AuthMenuActionDO madb : list) {
				madb.setMenuId(menuId);
				newList.add(madb);
			}
			authMenuActionDAO.batchInsertAuthMenuAction(newList);
		}

	}

	@Override
	public void updateAuthMenu(AuthMenuDO db, Long operatorId, String actions, String deletedActions) throws Exception {

		// 检验删除的功能是否配置给了某些角色
		checkUsedAction(db, deletedActions);

		// 获取功能列表
		List<AuthMenuActionDO> list = getActionList(db, operatorId, actions);

		// 更新菜单基本信息
		authMenuDAO.updateAuthMenu(db);

		// 删除菜单对应的所有功能
		authMenuActionDAO.deleteAuthMenuActionByMenuIdAndSysId(db.getId(), db.getSysId());

		// 批量添加新的功能
		if (!CollectionUtils.isEmpty(list)) {
			authMenuActionDAO.batchInsertAuthMenuAction(list);
		}
	}

	private void checkUsedAction(AuthMenuDO db, String deletedActions) throws Exception {

		if (StringUtils.isNotEmpty(deletedActions)) {
			List<AuthRoleMenuDO> rmList = authRoleMenuDAO.queryRoleMenuBySysIdAndMenuId(db.getSysId(), db.getId());
			for (AuthRoleMenuDO rmdb : rmList) {
				String actionIds =  rmdb.getActionIds();
				if(!StringUtils.isEmpty(actionIds)){
					String[] cActions = rmdb.getActionIds().split(",");
					for (String s : cActions) {
						if (Arrays.asList(deletedActions.split(",")).contains(s)) {
							throw new Exception("删除的功能已被角色占用！！");
						}
					}
				}
				
			}
		}
	}

	private List<AuthMenuActionDO> getActionList(AuthMenuDO db, Long operatorId, String actions) {

		List<AuthMenuActionDO> list = Lists.newArrayList();

		if (StringUtils.isNotEmpty(actions)) {
			for (String s : actions.split(AuthConstants.SEPARATOR_COMMA)) {
				AuthMenuActionDO adb = new AuthMenuActionDO();
				adb.setActionId(NumberUtils.toLong(s));
				adb.setMenuId(db.getId());
				adb.setOperatorId(operatorId);
				adb.setSysId(db.getSysId());
				list.add(adb);
			}
		}

		return list;
	}

	@Override
	public int deleteAuthMenuByids(String ids,Long sysId) {
		if (StringUtil.isEmpty(ids)) {
			throw new OpServiceException("菜单id不能为空");
		}
		if (!ids.matches("^[\\d|]+$")) {
			throw new OpServiceException("菜单id不符合规范");
		}
		//校验id中是否存在子菜单
		List<String> idList = StringUtil.StringToStringList(ids, "[|]");
		List<AuthMenuDO> menuList = null;
		List<AuthRoleMenuDO> authRoleMenuList = null;
		for (String id : idList) {
			menuList = authMenuDAO.queryAuthMenuByParentId(Long.parseLong(id));
			if (menuList.size() > 0) {
				throw new OpServiceException("部分菜单包含子菜单，请先删除对应子菜单");
			}
			//查看角色权限是否已引用当前菜单权限
			authRoleMenuList = authRoleMenuDAO.queryRoleMenuBySysIdAndMenuId(sysId, Long.parseLong(id));
			if (authRoleMenuList.size() > 0) {
				throw new OpServiceException("部分菜单已被角色权限引用，请先取消当前菜单引用");
			}
		}
		ids = ids.replace("|", ",");
		return authMenuDAO.deleteAuthMenuByIds(ids);
	}
	
	@Override
	public List<AuthMenuDO> listAuthMenuBySysId(Long sysId) {

		return authMenuDAO.queryAuthMenuBySysId(sysId);
	}

	@Override
	public List<AuthActionDO> listAuthActionByMenuIdAndSysId(Long menuId, Long sysId) {

		List<AuthActionDO> list = Lists.newArrayList();

		List<AuthMenuActionDO> maList = authMenuActionDAO.queryAuthMenuActionByMenuIdAndSysId(menuId, sysId);

		if (!CollectionUtils.isEmpty(maList)) {
			for (AuthMenuActionDO db : maList) {
				list.add(authActionDAO.queryAuthActionById(db.getActionId()));
			}
			return list;
		}

		return null;
	}

	@Override
	public Node treeAuthMenuBySysId(Long sysId) {

		List<AuthMenuDO> menuList = authMenuDAO.queryAuthMenuBySysId(sysId);

		List<Node> nodes = NodeUtils.menuToNode(menuList);

		return NodeUtils.sortChildren(NodeUtils.generateNodeTree(nodes, AuthConstants.ROOT));
	}

	@Override
	public AuthMenuVO getAuthMenuDetailById(Long id) throws Exception {

		return CommonUtils.dbToVo(authMenuDAO.queryAuthMenuById(id), AuthMenuVO.class, new CallBack<AuthMenuDO, AuthMenuVO>() {

			@Override
			public void execute(AuthMenuDO db, AuthMenuVO vo) {

				if (AuthConstants.ROOT.equals(db.getParentId().toString())) {
					vo.setParentName("根节点"); // 根节点
				} else {
					vo.setParentName(authMenuDAO.queryNameById(db.getParentId())); // 父节点名称
				}

				vo.setStatusText(StatusEnum.valueOf(db.getStatus())); // 状态文本

				StringBuffer actionNames = new StringBuffer();

				List<AuthActionDO> aList = listAuthActionByMenuIdAndSysId(db.getId(), db.getSysId());
				List<IdNameVO> actions = Lists.newArrayList();

				if (!CollectionUtils.isEmpty(aList)) {
					for (AuthActionDO adb : aList) {
						actionNames.append(adb.getName() + ",");
						actions.add(new IdNameVO(adb.getId(), adb.getName()));
					}
					vo.setActions(actions); // 功能集合
					vo.setActionNames(actionNames.substring(0, actionNames.length() - 1)); // 功能名称列表
				}

				vo.setOperator(userDAO.queryUserNameById(vo.getOperatorId())); //
			}

		});

	}

	@Override
	public AuthMenuDO getAuthMenuById(Long id) throws Exception {

		return authMenuDAO.queryAuthMenuById(id);
	}

	@Override
	public void removeAuthMenu(Long menuId, Long sysId) {
		
		//把子节点的父节点指向自己的父节点
		AuthMenuDO db = authMenuDAO.queryAuthMenuById(menuId);
		List<AuthMenuDO> mList = authMenuDAO.queryAuthMenuByParentId(menuId);
		StringBuffer ids = new StringBuffer();
		if(!CollectionUtils.isEmpty(mList)){
			for(AuthMenuDO mdb : mList){
				ids.append(mdb.getId()).append(",");
			}
			authMenuDAO.batchUpdateParentId(ids.substring(0,ids.length()-1), db.getParentId());
		}
		
		
		//删除角色菜单关系 
		 authRoleMenuDAO.batchDeleteAuthRoleMenuByMenuIdAndSysId(menuId, sysId);
		
		 //删除菜单功能关系 
		authMenuActionDAO.deleteAuthMenuActionByMenuIdAndSysId(menuId, sysId);
		 
		//删除菜单
		authMenuDAO.deleteAuthMenu(menuId);
	}

	@Override
	public void removeAuthMenuTree(Long menuId, Long sysId) {
		
		List<AuthMenuDO> mList = Lists.newArrayList();
		
		if(AuthConstants.ROOT.equals(menuId.toString())){
			mList = authMenuDAO.queryAuthMenuBySysId(sysId);
		}else{
			getSubMenuList(menuId,mList);
		}
		
		//先删除子节点
		for(AuthMenuDO mdb : mList){
			removeAuthMenu(mdb.getId(), sysId);
		}
		
		//再删除当前节点
		removeAuthMenu(menuId, sysId);
	}   

	/**
	 * 获取所有子菜单节点
	 * @param parentId 父节点id
	 * @param destList 子菜单节点列表
	 */
	private void  getSubMenuList(Long parentId,List<AuthMenuDO> destList){
		List<AuthMenuDO> mList = authMenuDAO.queryAuthMenuByParentId(parentId);
		if(!CollectionUtils.isEmpty(mList)){
			for(AuthMenuDO mdb : mList){
				destList.add(mdb);
				getSubMenuList(mdb.getId(), destList);
			}
		}
	}

	/**
	 * 新增权限菜单
	 */
	@Override
	public void saveAuthMenu(AuthMenuVO authMenuVo) throws Exception {
		checkNull(authMenuVo, "name","code","url");
		checkCodeIsExist(authMenuVo.getCode(),authMenuVo.getSysId(),null);
		if (authMenuVo.getIsLeaf() == null) {
			authMenuVo.setIsLeaf(0);
		}
		if (authMenuVo.getParentId() == null) {
			authMenuVo.setParentId(0L);
		}
		if (authMenuVo.getMenutype() == null) {
			authMenuVo.setMenutype(1);
		}
		authMenuVo.setStatus(1);
		insertOrUpdate(authMenuVo,true);
	}
	/**
	 * <p>Title: checkCodeIsExist</p>
	 * <p> 功能描述:校验菜单code是否已存在 </p>
	 * @param code
	 * @param sysId
	 */
	private void checkCodeIsExist(String code,Long sysId,Long menuId) {
		Long id = authMenuDAO.queryAuthMenuByCodeAndSysId(code, sysId);
		if (menuId != null) {
			if (id != null && !String.valueOf(id).equals(String.valueOf(menuId))) {
				throw new OpServiceException("菜单code已存在"); 
			}
		}else {
			if (id != null) {
				throw new OpServiceException("菜单code已存在"); 
			}
		}
	}
	
	private void insertOrUpdate(AuthMenuVO authMenuVo,boolean isInsert) {
		AuthMenuDO authMenuDo = new AuthMenuDO();
		authMenuDo.setCode(authMenuVo.getCode());
		if (isInsert) {
			authMenuDo.setGmtCreate(new Date());
		}
		authMenuDo.setGmtModified(new Date());
		authMenuDo.setIcon(authMenuVo.getIcon());
		authMenuDo.setIsLeaf(authMenuVo.getIsLeaf());
		authMenuDo.setName(authMenuVo.getName());
		authMenuDo.setNum(authMenuVo.getNum());
		authMenuDo.setOperatorId(authMenuVo.getOperatorId());
		authMenuDo.setParentId(authMenuVo.getParentId());
		authMenuDo.setStatus(authMenuVo.getStatus());
		authMenuDo.setSysId(authMenuVo.getSysId());
		authMenuDo.setUrl(authMenuVo.getUrl());
		authMenuDo.setMenutype(authMenuVo.getMenutype());
		if (isInsert) {
			authMenuDAO.insertAuthMenu(authMenuDo);
		}else {
			authMenuDo.setId(authMenuVo.getId());
			authMenuDAO.updateBySelective(authMenuDo);
		}
	}
	
	private void checkNull(AuthMenuVO authMenuVo,String... str) {
		SuixunAssert.assertNull(authMenuVo, "必要参数不能为空");
		for (String tempStr : str) {
			if ("id".equals(tempStr)) {
				SuixunAssert.assertNull(authMenuVo.getId(), "菜单id不能为空");
			}
			if ("name".equals(tempStr)) {
				SuixunAssert.assertBlank(authMenuVo.getName(), "权限名称不能为空");
			}
			if ("code".equals(tempStr)) {
				SuixunAssert.assertBlank(authMenuVo.getCode(), "菜单code不能为空");
			}
			if ("url".equals(tempStr)) {
				SuixunAssert.assertBlank(authMenuVo.getUrl(), "资源url不能为空");
			}
		}
	}

	/**
	 * 修改权限菜单
	 */
	@Override
	public void updateAuthMenu(AuthMenuVO authMenuVo) throws Exception {
		checkNull(authMenuVo, "id");
		if (StringUtil.isNotEmpty(authMenuVo.getCode())) {
			checkCodeIsExist(authMenuVo.getCode(),authMenuVo.getSysId(),authMenuVo.getId());
		}
		insertOrUpdate(authMenuVo,false);
	}

	/**
	 * 分页查询权限菜单
	 */
	@Override
	public PageResult<AuthMenuVO> listAuthMenu(AuthMenuQuery query) throws Exception {
		if (query.getIndex() == 0) {
			query.setIndex(1);
		}
		query.setIsDownload(true);
		List<AuthMenuVO> list = CommonUtils.dbToVo(authMenuDAO.queryAuthMenu(query),AuthMenuVO.class);
		query.setIsDownload(false);
		int count = authMenuDAO.queryAuthMenu(query).size();
		return PageResult.create(list, count, query.getIndex(), query.getNum());
	}
	
}
